home *** CD-ROM | disk | FTP | other *** search
/ Personal Computer World 2009 February / PCWFEB09.iso / Software / Linux / Kubuntu 8.10 / kubuntu-8.10-desktop-i386.iso / casper / filesystem.squashfs / usr / share / system-config-printer / monitor.pyc (.txt) < prev    next >
Python Compiled Bytecode  |  2008-10-29  |  16KB  |  566 lines

  1. # Source Generated with Decompyle++
  2. # File: in.pyc (Python 2.5)
  3.  
  4. import cups
  5. import dbus
  6. import dbus.glib as dbus
  7. import gobject
  8. import time
  9. from debug import *
  10. import pprint
  11.  
  12. _ = lambda x: x
  13.  
  14. def set_gettext_function(x):
  15.     _ = x
  16.  
  17. import statereason
  18. from statereason import StateReason
  19. statereason.set_gettext_function(_)
  20. CONNECTING_TIMEOUT = 60
  21. MIN_REFRESH_INTERVAL = 1
  22.  
  23. def state_reason_is_harmless(reason):
  24.     if reason.startswith('moving-to-paused') and reason.startswith('paused') and reason.startswith('shutdown') and reason.startswith('stopping') or reason.startswith('stopped-partly'):
  25.         return True
  26.     
  27.     return False
  28.  
  29.  
  30. def collect_printer_state_reasons(connection):
  31.     result = { }
  32.     
  33.     try:
  34.         printers = connection.getPrinters()
  35.     except cups.IPPError:
  36.         return result
  37.  
  38.     for name, printer in printers.iteritems():
  39.         reasons = printer['printer-state-reasons']
  40.         if type(reasons) != list:
  41.             reasons = [
  42.                 reasons]
  43.         
  44.         for reason in reasons:
  45.             if reason == 'none':
  46.                 break
  47.             
  48.             if state_reason_is_harmless(reason):
  49.                 continue
  50.             
  51.             if not result.has_key(name):
  52.                 result[name] = []
  53.             
  54.             result[name].append(StateReason(name, reason))
  55.         
  56.     
  57.     return result
  58.  
  59.  
  60. class Watcher:
  61.     
  62.     def monitor_exited(self, monitor):
  63.         debugprint(repr(monitor) + ' exited')
  64.  
  65.     
  66.     def state_reason_added(self, monitor, reason):
  67.         debugprint(repr(monitor) + ': +' + repr(reason))
  68.  
  69.     
  70.     def state_reason_removed(self, monitor, reason):
  71.         debugprint(repr(monitor) + ': -' + repr(reason))
  72.  
  73.     
  74.     def still_connecting(self, monitor, reason):
  75.         debugprint(repr(monitor) + ": `%s' still connecting" % reason.get_printer())
  76.  
  77.     
  78.     def now_connected(self, monitor, printer):
  79.         debugprint(repr(monitor) + ": `%s' now connected" % printer)
  80.  
  81.     
  82.     def current_printers_and_jobs(self, monitor, printers, jobs):
  83.         debugprint(repr(monitor) + ': printers and jobs lists provided')
  84.  
  85.     
  86.     def job_added(self, monitor, jobid, eventname, event, jobdata):
  87.         debugprint(repr(monitor) + ': job %d added' % jobid)
  88.  
  89.     
  90.     def job_event(self, monitor, jobid, eventname, event, jobdata):
  91.         debugprint(repr(monitor) + ": job %d has event `%s'" % (jobid, eventname))
  92.  
  93.     
  94.     def job_removed(self, monitor, jobid, eventname, event):
  95.         debugprint(repr(monitor) + ': job %d removed' % jobid)
  96.  
  97.     
  98.     def printer_added(self, monitor, printer):
  99.         debugprint(repr(monitor) + ": printer `%s' added" % printer)
  100.  
  101.     
  102.     def printer_event(self, monitor, printer, eventname, event):
  103.         debugprint(repr(monitor) + ": printer `%s' has event `%s'" % (printer, eventname))
  104.  
  105.     
  106.     def printer_removed(self, monitor, printer):
  107.         debugprint(repr(monitor) + ": printer `%s' removed" % printer)
  108.  
  109.     
  110.     def cups_connection_error(self, monitor):
  111.         debugprint(repr(monitor) + ': CUPS connection error')
  112.  
  113.     
  114.     def cups_ipp_error(self, monitor, e, m):
  115.         debugprint(repr(monitor) + ': CUPS IPP error (%d, %s)' % (e, repr(m)))
  116.  
  117.  
  118.  
  119. class Monitor:
  120.     DBUS_PATH = '/com/redhat/PrinterSpooler'
  121.     DBUS_IFACE = 'com.redhat.PrinterSpooler'
  122.     
  123.     def __init__(self, watcher, bus = None, my_jobs = True, specific_dests = None, monitor_jobs = True):
  124.         self.watcher = watcher
  125.         self.my_jobs = my_jobs
  126.         self.specific_dests = specific_dests
  127.         self.monitor_jobs = monitor_jobs
  128.         self.jobs = { }
  129.         self.printer_state_reasons = { }
  130.         self.printers = set()
  131.         self.which_jobs = 'not-completed'
  132.         self.reasons_seen = { }
  133.         self.connecting_timers = { }
  134.         self.still_connecting = set()
  135.         self.connecting_to_device = { }
  136.         self.received_any_dbus_signals = False
  137.         if bus == None:
  138.             bus = dbus.SystemBus()
  139.         
  140.         bus.add_signal_receiver(self.handle_dbus_signal, path = self.DBUS_PATH, dbus_interface = self.DBUS_IFACE)
  141.         self.bus = bus
  142.         self.sub_id = -1
  143.         self.refresh()
  144.  
  145.     
  146.     def get_jobs(self):
  147.         return self.jobs.copy()
  148.  
  149.     
  150.     def cleanup(self):
  151.         if self.sub_id != -1:
  152.             
  153.             try:
  154.                 c = cups.Connection()
  155.                 c.cancelSubscription(self.sub_id)
  156.                 debugprint('Canceled subscription %d' % self.sub_id)
  157.  
  158.         
  159.         self.bus.remove_signal_receiver(self.handle_dbus_signal, path = self.DBUS_PATH, dbus_interface = self.DBUS_IFACE)
  160.         self.watcher.monitor_exited(self)
  161.  
  162.     
  163.     def check_still_connecting(self, printer):
  164.         '''Timer callback to check on connecting-to-device reasons.'''
  165.         del self.connecting_timers[printer]
  166.         debugprint("Still-connecting timer fired for `%s'" % printer)
  167.         (printer_jobs, my_printers) = self.sort_jobs_by_printer()
  168.         self.update_connecting_devices(printer_jobs)
  169.         return False
  170.  
  171.     
  172.     def update_connecting_devices(self, printer_jobs = { }):
  173.         '''Updates connecting_to_device dict and still_connecting set.'''
  174.         time_now = time.time()
  175.         connecting_to_device = { }
  176.         trouble = False
  177.         for printer, reasons in self.printer_state_reasons.iteritems():
  178.             connected = True
  179.             for reason in reasons:
  180.                 if reason.get_reason() == 'connecting-to-device':
  181.                     have_processing_job = False
  182.                     for job, data in printer_jobs.get(printer, { }).iteritems():
  183.                         state = data.get('job-state', cups.IPP_JOB_CANCELED)
  184.                         if state == cups.IPP_JOB_PROCESSING:
  185.                             have_processing_job = True
  186.                             break
  187.                             continue
  188.                     
  189.                     if not have_processing_job:
  190.                         debugprint('Ignoring stale connecting-to-device x')
  191.                         continue
  192.                     
  193.                     printer = reason.get_printer()
  194.                     t = self.connecting_to_device.get(printer, time_now)
  195.                     connecting_to_device[printer] = t
  196.                     debugprint('Connecting time: %d' % (time_now - t))
  197.                     if time_now - t >= CONNECTING_TIMEOUT:
  198.                         if have_processing_job:
  199.                             self.still_connecting.add(printer)
  200.                             self.watcher.still_connecting(self, reason)
  201.                             if self.connecting_timers.has_key(printer):
  202.                                 gobject.source_remove(self.connecting_timers[printer])
  203.                                 del self.connecting_timers[printer]
  204.                                 debugprint("Stopped connecting timer for `%s'" % printer)
  205.                             
  206.                         
  207.                     
  208.                     connected = False
  209.                     break
  210.                     continue
  211.             
  212.             if connected and self.connecting_timers.has_key(printer):
  213.                 gobject.source_remove(self.connecting_timers[printer])
  214.                 del self.connecting_timers[printer]
  215.                 debugprint("Stopped connecting timer for `%s'" % printer)
  216.                 continue
  217.         
  218.         remove = set()
  219.         for printer in self.still_connecting:
  220.             if not connecting_to_device.has_key(printer):
  221.                 remove.add(printer)
  222.                 self.watcher.now_connected(self, printer)
  223.                 if self.connecting_timers.has_key(printer):
  224.                     gobject.source_remove(self.connecting_timers[printer])
  225.                     del self.connecting_timers[printer]
  226.                     debugprint("Stopped connecting timer for `%s'" % printer)
  227.                 
  228.             self.connecting_timers.has_key(printer)
  229.         
  230.         self.still_connecting = self.still_connecting.difference(remove)
  231.         self.connecting_to_device = connecting_to_device
  232.  
  233.     
  234.     def check_state_reasons(self, my_printers = set(), printer_jobs = { }):
  235.         old_reasons_seen_keys = self.reasons_seen.keys()
  236.         reasons_now = set()
  237.         for printer, reasons in self.printer_state_reasons.iteritems():
  238.             for reason in reasons:
  239.                 tuple = reason.get_tuple()
  240.                 printer = reason.get_printer()
  241.                 reasons_now.add(tuple)
  242.                 if not self.reasons_seen.has_key(tuple):
  243.                     self.watcher.state_reason_added(self, reason)
  244.                     self.reasons_seen[tuple] = reason
  245.                 
  246.                 if reason.get_reason() == 'connecting-to-device' and not self.connecting_to_device.has_key(printer):
  247.                     have_processing_job = False
  248.                     for job, data in printer_jobs.get(printer, { }).iteritems():
  249.                         state = data.get('job-state', cups.IPP_JOB_CANCELED)
  250.                         if state == cups.IPP_JOB_PROCESSING:
  251.                             have_processing_job = True
  252.                             break
  253.                             continue
  254.                     
  255.                     if have_processing_job:
  256.                         t = gobject.timeout_add((1 + CONNECTING_TIMEOUT) * 1000, self.check_still_connecting, printer)
  257.                         self.connecting_timers[printer] = t
  258.                         debugprint("Start connecting timer for `%s'" % printer)
  259.                     else:
  260.                         debugprint('Ignoring stale connecting-to-device')
  261.                         debugprint(pprint.pformat(printer_jobs))
  262.                 have_processing_job
  263.             
  264.         
  265.         self.update_connecting_devices(printer_jobs)
  266.         items = self.reasons_seen.keys()
  267.         for tuple in items:
  268.             if tuple not in reasons_now:
  269.                 reason = self.reasons_seen[tuple]
  270.                 del self.reasons_seen[tuple]
  271.                 self.watcher.state_reason_removed(self, reason)
  272.                 continue
  273.         
  274.  
  275.     
  276.     def get_notifications(self):
  277.         debugprint('get_notifications')
  278.         
  279.         try:
  280.             c = cups.Connection()
  281.             
  282.             try:
  283.                 
  284.                 try:
  285.                     notifications = c.getNotifications([
  286.                         self.sub_id], [
  287.                         self.sub_seq + 1])
  288.                 except AttributeError:
  289.                     notifications = c.getNotifications([
  290.                         self.sub_id])
  291.  
  292.             except cups.IPPError:
  293.                 (e, m) = None
  294.                 if e == cups.IPP_NOT_FOUND:
  295.                     self.sub_id = -1
  296.                     self.refresh()
  297.                     return False
  298.                 
  299.                 self.watcher.cups_ipp_error(self, e, m)
  300.                 return True
  301.  
  302.         except RuntimeError:
  303.             self.watcher.cups_connection_error(self)
  304.             return True
  305.  
  306.         deferred_calls = []
  307.         jobs = self.jobs.copy()
  308.         for event in notifications['events']:
  309.             seq = event['notify-sequence-number']
  310.             
  311.             try:
  312.                 if seq <= self.sub_seq:
  313.                     continue
  314.             except AttributeError:
  315.                 pass
  316.  
  317.             self.sub_seq = seq
  318.             nse = event['notify-subscribed-event']
  319.             debugprint('%d %s %s' % (seq, nse, event['notify-text']))
  320.             debugprint(pprint.pformat(event))
  321.             if nse.startswith('printer-'):
  322.                 name = event['printer-name']
  323.                 if nse == 'printer-added' and name not in self.printers:
  324.                     self.printers.add(name)
  325.                     deferred_calls.append((self.watcher.printer_added, (self, name)))
  326.                     continue
  327.                 if nse == 'printer-deleted' and name in self.printers:
  328.                     self.printers.remove(name)
  329.                     items = self.reasons_seen.keys()
  330.                     for tuple in items:
  331.                         if tuple[1] == name:
  332.                             reason = self.reasons_seen[tuple]
  333.                             del self.reasons_seen[tuple]
  334.                             deferred_calls.append((self.watcher.state_reason_removed, (self, reason)))
  335.                             continue
  336.                     
  337.                     if self.printer_state_reasons.has_key(name):
  338.                         del self.printer_state_reasons[name]
  339.                     
  340.                     deferred_calls.append((self.watcher.printer_removed, (self, name)))
  341.                     continue
  342.                 if name in self.printers:
  343.                     printer_state_reasons = event['printer-state-reasons']
  344.                     if type(printer_state_reasons) != list:
  345.                         printer_state_reasons = [
  346.                             printer_state_reasons]
  347.                     
  348.                     reasons = []
  349.                     for reason in printer_state_reasons:
  350.                         if reason == 'none':
  351.                             break
  352.                         
  353.                         if state_reason_is_harmless(reason):
  354.                             continue
  355.                         
  356.                         reasons.append(StateReason(name, reason))
  357.                     
  358.                     self.printer_state_reasons[name] = reasons
  359.                     deferred_calls.append((self.watcher.printer_event, (self, name, nse, event)))
  360.                     continue
  361.                 continue
  362.             
  363.             jobid = event['notify-job-id']
  364.             if (nse == 'job-created' or nse == 'job-state-changed') and not jobs.has_key(jobid) and event['job-state'] == cups.IPP_JOB_PROCESSING:
  365.                 if self.specific_dests != None and event['printer-name'] not in self.specific_dests:
  366.                     continue
  367.                 
  368.                 
  369.                 try:
  370.                     attrs = c.getJobAttributes(jobid)
  371.                     if self.my_jobs and attrs['job-originating-user-name'] != cups.getUser():
  372.                         continue
  373.                     
  374.                     jobs[jobid] = attrs
  375.                 except AttributeError:
  376.                     jobs[jobid] = {
  377.                         'job-k-octets': 0 }
  378.                 except cups.IPPError:
  379.                     (e, m) = None
  380.                     self.watcher.cups_ipp_error(self, e, m)
  381.                     jobs[jobid] = {
  382.                         'job-k-octets': 0 }
  383.  
  384.                 deferred_calls.append((self.watcher.job_added, (self, jobid, nse, event, jobs[jobid].copy())))
  385.             elif nse == 'job-completed':
  386.                 
  387.                 try:
  388.                     del jobs[jobid]
  389.                     deferred_calls.append((self.watcher.job_removed, (self, jobid, nse, event)))
  390.                 continue
  391.                 except KeyError:
  392.                     continue
  393.                 
  394.  
  395.             
  396.             
  397.             try:
  398.                 job = jobs[jobid]
  399.             except KeyError:
  400.                 continue
  401.  
  402.             for attribute in [
  403.                 'job-state',
  404.                 'job-name']:
  405.                 job[attribute] = event[attribute]
  406.             
  407.             if event.has_key('notify-printer-uri'):
  408.                 job['job-printer-uri'] = event['notify-printer-uri']
  409.             
  410.             deferred_calls.append((self.watcher.job_event, (self, jobid, nse, event, job.copy())))
  411.         
  412.         self.update(jobs)
  413.         self.jobs = jobs
  414.         for fn, args in deferred_calls:
  415.             fn(*args)
  416.         
  417.         if not self.received_any_dbus_signals:
  418.             gobject.source_remove(self.update_timer)
  419.             interval = 1000 * notifications['notify-get-interval']
  420.             self.update_timer = gobject.timeout_add(interval, self.get_notifications)
  421.         
  422.         return False
  423.  
  424.     
  425.     def refresh(self):
  426.         debugprint('refresh')
  427.         
  428.         try:
  429.             c = cups.Connection()
  430.         except RuntimeError:
  431.             self.watcher.cups_connection_error(self)
  432.             return None
  433.  
  434.         if self.sub_id != -1:
  435.             
  436.             try:
  437.                 c.cancelSubscription(self.sub_id)
  438.             except cups.IPPError:
  439.                 (e, m) = None
  440.                 self.watcher.cups_ipp_error(self, e, m)
  441.  
  442.             gobject.source_remove(self.update_timer)
  443.             debugprint('Canceled subscription %d' % self.sub_id)
  444.         
  445.         
  446.         try:
  447.             del self.sub_seq
  448.         except AttributeError:
  449.             pass
  450.  
  451.         events = [
  452.             'printer-added',
  453.             'printer-deleted',
  454.             'printer-state-changed']
  455.         if self.monitor_jobs:
  456.             events.extend([
  457.                 'job-created',
  458.                 'job-completed',
  459.                 'job-stopped',
  460.                 'job-progress',
  461.                 'job-state-changed'])
  462.         
  463.         
  464.         try:
  465.             self.sub_id = c.createSubscription('/', events = events)
  466.         except cups.IPPError:
  467.             (e, m) = None
  468.             self.watcher.cups_ipp_error(self, e, m)
  469.  
  470.         self.update_timer = gobject.timeout_add(MIN_REFRESH_INTERVAL * 1000, self.get_notifications)
  471.         debugprint('Created subscription %d' % self.sub_id)
  472.         
  473.         try:
  474.             if self.monitor_jobs:
  475.                 jobs = c.getJobs(which_jobs = self.which_jobs, my_jobs = self.my_jobs)
  476.             else:
  477.                 jobs = { }
  478.             self.printer_state_reasons = collect_printer_state_reasons(c)
  479.             dests = c.getDests()
  480.             printers = set()
  481.             for printer, instance in dests.keys():
  482.                 if printer == None:
  483.                     continue
  484.                 
  485.                 if instance != None:
  486.                     continue
  487.                 
  488.                 printers.add(printer)
  489.             
  490.             self.printers = printers
  491.         except cups.IPPError:
  492.             (e, m) = None
  493.             self.watcher.cups_ipp_error(self, e, m)
  494.             return None
  495.         except RuntimeError:
  496.             self.watcher.cups_connection_error(self)
  497.             return None
  498.  
  499.         if self.specific_dests != None:
  500.             for jobid in jobs.keys():
  501.                 uri = jobs[jobid].get('job-printer-uri', '/')
  502.                 i = uri.rfind('/')
  503.                 printer = uri[i + 1:]
  504.                 if printer not in self.specific_dests:
  505.                     del jobs[jobid]
  506.                     continue
  507.             
  508.         
  509.         self.watcher.current_printers_and_jobs(self, self.printers.copy(), jobs.copy())
  510.         self.update(jobs)
  511.         self.jobs = jobs
  512.         return False
  513.  
  514.     
  515.     def sort_jobs_by_printer(self, jobs = None):
  516.         if jobs == None:
  517.             jobs = self.jobs
  518.         
  519.         my_printers = set()
  520.         printer_jobs = { }
  521.         for job, data in jobs.iteritems():
  522.             state = data.get('job-state', cups.IPP_JOB_CANCELED)
  523.             if state >= cups.IPP_JOB_CANCELED:
  524.                 continue
  525.             
  526.             uri = data.get('job-printer-uri', '')
  527.             i = uri.rfind('/')
  528.             if i == -1:
  529.                 continue
  530.             
  531.             printer = uri[i + 1:]
  532.             my_printers.add(printer)
  533.             if not printer_jobs.has_key(printer):
  534.                 printer_jobs[printer] = { }
  535.             
  536.             printer_jobs[printer][job] = data
  537.         
  538.         return (printer_jobs, my_printers)
  539.  
  540.     
  541.     def update(self, jobs):
  542.         debugprint('update')
  543.         (printer_jobs, my_printers) = self.sort_jobs_by_printer(jobs)
  544.         self.check_state_reasons(my_printers, printer_jobs)
  545.  
  546.     
  547.     def handle_dbus_signal(self, *args):
  548.         gobject.source_remove(self.update_timer)
  549.         self.update_timer = gobject.timeout_add(200, self.get_notifications)
  550.         if not self.received_any_dbus_signals:
  551.             self.received_any_dbus_signals = True
  552.         
  553.  
  554.  
  555. if __name__ == '__main__':
  556.     set_debugging(True)
  557.     m = Monitor(Watcher())
  558.     loop = gobject.MainLoop()
  559.     
  560.     try:
  561.         loop.run()
  562.     finally:
  563.         m.cleanup()
  564.  
  565.  
  566.